﻿using System;
using System.ComponentModel;
using ActionLogic;
using BehaviorModel.Block.RandomGenerator;
using Model;
using Model.Block;
using log4net;

namespace BehaviorModel.Block.Generator
{
    /// <summary>
    /// Describes implementation of Generator.
    /// </summary>
    public class Generator : IGenerator
    {
        #region Fields

        private static readonly ILog logger = LogManager.GetLogger(typeof(Generator));

        private Request _nextRequest;
        private RandomGeneratorType _generatorType;
        private IReceiverBlock _receiverBlock;

        #endregion

        #region Properties

        #region IBlock

        public Guid Id { get; private set; }

        public BlockType Type
        {
            get { return BlockType.Generator; }
        }

        #endregion

        #region IExecutable

        public Action Execute
        {
            get { return Generate; }
        }

        public double Priority
        {
            get { return _nextRequest.InitTime; }
        }

        public bool IsBackground
        {
            get { return false; }
        }

        public event Action<IExecutable> OnActionExecuted;

        #endregion

        #region IGenerator

        public int StartTime { get; set; }

        public GeneratorStorage Storage { get; private set; }

        public IRandomGenerator RequestGenerator { get; private set; }

        public RandomGeneratorType GeneratorType
        {
            get { return _generatorType; }
            set
            {
                if (_generatorType == value)
                {
                    return;
                }
                
                _generatorType = value;
                RequestGenerator = RandomGeneratorFactory.GetInstance().Create(_generatorType);
                RaisePropertyChanged("GeneratorType");
            }
        }

        #endregion

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #endregion

        #region Constructor

        public Generator()
        {
            Id = Guid.NewGuid();
            Storage = new GeneratorStorage();

            GeneratorType = RandomGeneratorType.Poisson;
            RequestGenerator = RandomGeneratorFactory.GetInstance().Create(_generatorType);
            StartTime = 0;
        }

        #endregion

        #region Methods

        private void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region IBlock

        public void Initialize()
        {
            Storage = new GeneratorStorage();

            _nextRequest = RequestPool.GetInstance().GetFreeRequest();
            _nextRequest.InitTime = StartTime;

            logger.Info("INIT: " + GetType());

            if (OnActionExecuted != null)
            {
                OnActionExecuted(this);
            }
        }

        #endregion

        #region ISenderBlock

        public void ConnectReceiverBlock(IReceiverBlock receiverBlock)
        {
            //logger.Info("Generator is adding ReceivindBlock" + block.GetType().ToString() + " with id: " + block.Id);
            _receiverBlock = receiverBlock;
        }

        public void DisconnectReceiverBlock(IReceiverBlock receiverBlock)
        {
            _receiverBlock = null;
        }

        public void Send(Request request)
        {
            if (_receiverBlock == null) return;
            _receiverBlock.Receive(request);
        }

        #endregion

        public void Generate()
        {
            Send(_nextRequest);
            GenerateNextRequest();

            logger.Info("GENERATE REQUEST; INITTIME = " + _nextRequest.InitTime);

            if (OnActionExecuted != null)
            {
                OnActionExecuted(this);
            }
        }

        /// <summary>
        /// Generates next request.
        /// </summary>
        private void GenerateNextRequest()
        {
            var nextInitTime = RequestGenerator.GetNext(_nextRequest.InitTime);

            _nextRequest = RequestPool.GetInstance().GetFreeRequest();
            _nextRequest.InitTime = nextInitTime;

            Storage.CountOfGenerated++;
        }

        #endregion

    }
}
